package repository;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Scanner;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;


/**
 * Handler for parsing of music xml files within repository, more info in sax documentation.
 * @author 0800542m
 *
 */
public class FileParser implements ContentHandler{
	
	public static final int[] frequencies = {262,277,294,311,330,349,370,392,415,440,466,494};
	public static final char[] notes = {'c','C','d','D','e','f','F','g','G','a','A','b'};
	
	//Music for each instrument is added to this hashtable as they are parsed.
	private Hashtable<Integer, ArrayList<ArrayList<Note>>> musicResults;
	
	//Length of song.
	private int sLength;
	
	//Number of instruments.
	private int instruments;
	
	//Used to keep track of whether or not characters should be being read.
	private boolean inSequence;
	
	//Used to keep track of current instrument, octave and bar.
	private int instrument;
	private int octave;
	private int bar;
	
	
	//Constructor
	public FileParser(){
		musicResults = new Hashtable<Integer, ArrayList<ArrayList<Note>>>();
		inSequence = false;
		bar = 0;
	}
	
	
	//Methods used by package
	protected Hashtable<Integer, ArrayList<ArrayList<Note>>> getMusicResults(){
		return musicResults;
	}
	
	protected int getSongLength(){
		return sLength;
	}
	
	protected int getInstruments() {
		return instruments;
	}

	
	//Private methods
	
	/**
	 * Adds a string of notes to the current instrument's record in musicResults.
	 * @param notes String of notes read in from a sequence block.
	 */
	private void addNotes(String notes){
		Scanner sScan = new Scanner(notes);
		//Read in character by character.
		sScan.useDelimiter("");
		//Loops through all notes in notes.
		while(sScan.hasNext()){
			//Fetches the note.
			String note = sScan.next();
			int freq = 0;
			int i = 0;
			while(i < FileParser.notes.length){
				if(note.charAt(0) == FileParser.notes[i]){
					freq = FileParser.frequencies[i];
					break;
				}
				i++;
			}
			
			//Fetches the duration of the note.
			String duration = "";
			while(sScan.hasNextInt()){
				duration+=sScan.next();
			}
			
			boolean rest = false;
			if(note.equals("!"))
				rest = true;
			//Makes a new note out of all the retrieved info and adds it to the list of notes
			//for the current instrument.
			Note rNote = new Note(octave, freq, Integer.decode(duration), rest);
				musicResults.get(instrument).get(bar).add(rNote);
						
		}		
	}
	
	//Methods demanded by ContentHandler interface.
	//Used.
	
	public void startElement(String uri, String localName, String qName,
			Attributes atts) throws SAXException {
		
		//Takes in song length from song tag.
		if(qName.equalsIgnoreCase("song")){
			sLength = Integer.parseInt(atts.getValue("length"));
			instruments = Integer.parseInt(atts.getValue("instruments"));
		}
		//Updates instrument to that of the music being entered.
		if(qName.equalsIgnoreCase("music")){
			instrument = Integer.parseInt(atts.getValue("instrument"));
			musicResults.put(instrument, new ArrayList<ArrayList<Note>>());
		}
		//Increments bar whenever a new bar is reached.
		if(qName.equalsIgnoreCase("bar")){
			musicResults.get(instrument).add(new ArrayList<Note>());
		}
		//Updates octave to that of the sequence being entered.
		else if(qName.equalsIgnoreCase("sequence")){
			octave = Integer.parseInt(atts.getValue("octave"));
			inSequence = true;
		}		
	}
	
	//Should the parser be in a sequence block then read characters.
	public void characters(char[] ch, int start, int length)
				throws SAXException {
		if(inSequence)
			addNotes(new String(ch, start, length));			
	}
	
	//Marks when a sequence is left.
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			if(qName.equalsIgnoreCase("sequence"))
				inSequence = false;	
			if(qName.equalsIgnoreCase("bar"))
				bar++;
			if(qName.equalsIgnoreCase("music"))
				bar = 0;
		}
	
	
	
	
	//Unused and unnecessary, here for the purpose of satisfying the interface.  Ignore.
	@Override
	public void setDocumentLocator(Locator locator) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void startDocument() throws SAXException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void endDocument() throws SAXException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void startPrefixMapping(String prefix, String uri)
			throws SAXException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void endPrefixMapping(String prefix) throws SAXException {
		// TODO Auto-generated method stub
		
	}
	

	@Override
	public void ignorableWhitespace(char[] ch, int start, int length)
			throws SAXException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void processingInstruction(String target, String data)
			throws SAXException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void skippedEntity(String name) throws SAXException {
		// TODO Auto-generated method stub
		
	}

}
